package com.jzo2o.health.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.expcetions.ServerErrorException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.health.constant.RedisConstants;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.model.OrderUpdateStatusDTO;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.Member;
import com.jzo2o.health.model.domain.Orders;
import com.jzo2o.health.model.domain.Setmeal;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.OrdersPayResDTO;
import com.jzo2o.health.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.health.properties.TradeProperties;
import com.jzo2o.health.service.*;
import com.jzo2o.rabbitmq.client.RabbitClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 订单创建相关业务层
 * @author JIAN
 */
@Slf4j
@Service
public class OrderCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrderCreateService {
    @Resource
    private IMemberService memberService;
    @Resource
    private ISetmealService setmealService;
    @Resource
    private IOrderCommonService ordersCommonService;
    @Resource
    private IReservationSettingService reservationSettingService;
    @Resource
    private TradingApi tradingApi;
    @Resource
    private NativePayApi nativePayApi;
    @Resource
    private TradeProperties tradeProperties;
    @Resource
    private RedisTemplate<String, Long> redisTemplate;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private RabbitClient rabbitClient;

    /**
     * 生成订单号(2位年+2位月+2位日+13位序号)
     */
    private Long generateOrderId() {
        Long idNo = redisTemplate.opsForValue().increment(RedisConstants.ORDER_ID_GENERATOR, 1);
        String orderId = DateUtils.format(LocalDateTime.now(), "yyMMdd") + String.format("%013d", idNo);
        return Long.valueOf(orderId);
    }

    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
        Long currentUserId = UserThreadLocal.currentUserId();
        if (ObjectUtils.isEmpty(currentUserId)) {
            throw new ForbiddenOperationException("用户信息不存在无法下单");
        }

        Member member = memberService.getById(currentUserId);
        if (ObjectUtils.isEmpty(member)) {
            throw new ForbiddenOperationException("用户信息不存在无法下单");
        }

        Setmeal setmeal = setmealService.getById(placeOrderReqDTO.getSetmealId());
        if (ObjectUtils.isEmpty(setmeal)) {
            throw new ForbiddenOperationException("套餐信息不存在无法下单");
        }

        // 生成订单id
        Long orderId = this.generateOrderId();
        if (ObjectUtils.isEmpty(orderId)) {
            throw new ServerErrorException("生成订单id失败无法下单");
        }

        transactionTemplate.executeWithoutResult(status -> {
            if (!SqlHelper.retBool(baseMapper.insert(Orders.builder()
                    .id(orderId)
                    .orderStatus(OrderStatusEnum.NO_PAY)
                    .payStatus(OrderPayStatusEnum.NO_PAY)
                    .setmealId(setmeal.getId())
                    .setmealName(setmeal.getName())
                    .setmealPrice(setmeal.getPrice())
                    .setmealSex(setmeal.getSex())
                    .setmealAge(setmeal.getAge())
                    .setmealImg(setmeal.getImg())
                    .setmealRemark(setmeal.getRemark())
                    .reservationDate(placeOrderReqDTO.getReservationDate())
                    .checkupPersonName(placeOrderReqDTO.getCheckupPersonName())
                    .checkupPersonSex(placeOrderReqDTO.getCheckupPersonSex())
                    .checkupPersonPhone(placeOrderReqDTO.getCheckupPersonPhone())
                    .checkupPersonIdcard(placeOrderReqDTO.getCheckupPersonIdcard())
                    .memberId(member.getId())
                    .memberPhone(member.getPhone())
                    .sortBy(DateUtils.toEpochMilli(LocalDateTime.now()))
                    .build()))) {
                throw new DBException("订单表插入失败下单失败");
            }

            // 已预约人数 + 1
            reservationSettingService.plusReservationCount(placeOrderReqDTO.getReservationDate(), 1);
        });

        // 发送消息到延时队列处理超时订单(发送检测的订单id)
        rabbitClient.sendMsg("health.exchange.topic.order", "order.overtime.in", orderId.toString());

        return new PlaceOrderResDTO(orderId);
    }

    @Override
    public OrdersPayResDTO payOrder(Long id, PayChannelEnum tradingChannel) {
        Orders orderInfo = baseMapper.selectById(id);
        if (ObjectUtils.isEmpty(orderInfo)) {
            throw new ForbiddenOperationException("订单不存在无法支付");
        }

        // 初步防止重复发起支付请求
        if (orderInfo.getPayStatus() == OrderPayStatusEnum.PAY_SUCCESS) {
            OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(orderInfo, OrdersPayResDTO.class);
            ordersPayResDTO.setProductOrderNo(id);
            return ordersPayResDTO;
        }

        // 下单
        String tradingChannelOld = orderInfo.getTradingChannel();
        NativePayResDTO nativePayResDTO = nativePayApi.createDownLineTrading(NativePayReqDTO.builder()
                .productOrderNo(id)
                .productAppId(IOrderCreateService.PRODUCT_APP_ID)
                .tradingChannel(tradingChannel)
                .tradingAmount(/*orderInfo.getSetmealPrice()*/ new BigDecimal("0.01"))
                .memo(orderInfo.getSetmealName())
                .changeChannel(ObjectUtils.isNotEmpty(tradingChannelOld)
                        && !tradingChannelOld.equals(tradingChannel.name()))
                .enterpriseId(tradingChannel == PayChannelEnum.WECHAT_PAY
                        ? tradeProperties.getWechatEnterpriseId()
                        : tradeProperties.getAliEnterpriseId())
                .build());

        if (!lambdaUpdate()
                .eq(Orders::getId, id)
                .set(Orders::getTradingOrderNo, nativePayResDTO.getTradingOrderNo())
                .set(Orders::getTradingChannel, nativePayResDTO.getTradingChannel())
                .update()) {
            log.warn("订单表更新失败(已正常生成支付二维码), 订单id: {}", id);
        }

        OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(nativePayResDTO, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        return ordersPayResDTO;
    }

    @Override
    public OrdersPayResDTO getPayResult(Long id) {
        Orders orders = baseMapper.selectById(id);
        if (ObjectUtils.isEmpty(orders)) {
            throw new CommonException("订单不存在");
        }

        // 公共返回参数
        OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(orders, OrdersPayResDTO.class);
        ordersPayResDTO.setProductOrderNo(id);

        // 未支付订单更新状态
        Long tradingOrderNo = orders.getTradingOrderNo();
        if (ObjectUtils.isNotEmpty(tradingOrderNo)
                && orders.getPayStatus() == OrderPayStatusEnum.NO_PAY) {
            TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(tradingOrderNo);

            // 支付成功
            if (ObjectUtils.isNotEmpty(tradingResDTO)
                    && tradingResDTO.getTradingState() == TradingStateEnum.YJS) {

                if (!ordersCommonService.updateStatus(OrderUpdateStatusDTO.builder()
                        .id(id)
                        .originStatus(OrderStatusEnum.NO_PAY)
                        .targetStatus(OrderStatusEnum.WAITING_CHECKUP)
                        .payStatus(OrderPayStatusEnum.PAY_SUCCESS)
                        .tradingOrderNo(tradingResDTO.getTradingOrderNo())
                        .transactionId(tradingResDTO.getTransactionId())
                        .tradingChannel(tradingResDTO.getTradingChannel())
                        .payTime(LocalDateTime.now())
                        .build())) {
                    throw new DBException("订单表更新失败");
                }

                // 支付成功更新最终响应信息的状态
                ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
            } else {
                // 未支付成功返回二维码
                ordersPayResDTO.setQrCode(tradingResDTO.getQrCode());
            }
        }

        return ordersPayResDTO;
    }
}